package com.davydov.suabl.model;

import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Queue;
import com.davydov.suabl.Position;
import com.davydov.suabl.model.OutsideEnvironmentException;

public class Environment {
	private int x_len, y_len;
	private Hashtable<Integer, PhysicsObject> ht;
	private Queue<KeyEvent> keys;
	
	Environment(int x_len, int y_len) {
		this.x_len = x_len;
		this.y_len = y_len;
		ht = new Hashtable<Integer, PhysicsObject>();
	}
	
	/*
	 * pre-cond: obj is not null
	 * post-cond: method adds the PhysicsObject passed into the Environment. If the position
	 * 		of the object is outside the environment, the method throws an exception.
	 */
	public synchronized void add(PhysicsObject obj) throws OutsideEnvironmentException {
		if(this.withinBounds(obj.getPosition()))
			ht.put(obj.getPosition().hashCode(), obj);
		else
			throw new OutsideEnvironmentException();
	}
	
	/*
	 * pre-cond: none
	 * post-cond: method removes obj from the environment and returns it. If the object is not
	 * 		in the environment, method returns null.
	 */
	public synchronized PhysicsObject remove(PhysicsObject obj) {
		return ht.remove(obj.getPosition().hashCode());
	}
	
	/*
	 * pre-cond: none
	 * post-cond: method returns a PhysicsObject at Position pos. If no object is at that
	 * 		position, method returns null.
	 */
	public synchronized PhysicsObject getObject(Position pos) {
		return ht.get(pos.hashCode());
	}
	
	/*
	 * pre-cond: none
	 * post-cond: Returns true only if Position pos is within the bounds of the environment as
	 * 		defined by this.getX() and this.getY().
	 */
	public synchronized boolean withinBounds(Position pos) {
		if((pos.getX() < this.getX_len()) && (pos.getY() < this.getY_len())
				&& (pos.getX() > 0) && (pos.getY() > 0))
			return true;
		return false;
	}
	
	/*
	 * pre-cond: none
	 * post-cond: method returns all elements contained in the environment inside and arraylist
	 */
	public synchronized ArrayList<PhysicsObject> getAllObjects() {
		ArrayList<PhysicsObject> objs = new ArrayList<PhysicsObject>();
		Enumeration<PhysicsObject> objs_enum = ht.elements();
		while(objs_enum.hasMoreElements()) {
			objs.add(objs_enum.nextElement());
		}
		return objs;
	}
	
	/*
	 * pre-cond: none
	 * post-cond: method adds a key event to the back of the queue.
	 */
	public void addKey(KeyEvent e) {
		synchronized(keys) {
			keys.add(e);
		}
	}
	
	/*
	 * pre-cond: none
	 * post-cond: method returns and removes the head from the queue, or null if the queue is empty.
	 */
	public KeyEvent pollKey() {
		synchronized(keys) {
			return keys.poll();
		}
	}
	
	/*
	 * pre-cond: none
	 * post-cond: method returns the head from the queue, or null if the queue is empty.
	 */
	public KeyEvent peekKey() {
		synchronized(keys) {
			return keys.peek();
		}
	}

	public synchronized int getX_len() {
		return x_len;
	}

	public synchronized void setX_len(int x_len) {
		this.x_len = x_len;
	}

	public synchronized int getY_len() {
		return y_len;
	}

	public synchronized void setY_len(int y_len) {
		this.y_len = y_len;
	}
}
